package org.whuims.web.document;

import com.alibaba.fastjson.JSON;
import org.apache.shiro.SecurityUtils;
import org.elasticsearch.common.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springside.modules.web.Servlets;
import org.whuims.entity.Document;
import org.whuims.entity.Paragraph;
import org.whuims.entity.vo.DocumentVo;
import org.whuims.service.account.ShiroDbRealm;
import org.whuims.service.document.DocumentService;
import org.whuims.service.document.DocumentSolrService;
import org.whuims.service.paragraph.ParagraphService;
import org.whuims.service.paragraph.ParagraphSolrService;
import org.whuims.web.util.DocumentExtractor;
import org.whuims.web.util.ParagraphExtractor;
import org.whuims.web.util.pdf2xml.Pdf2Xml;
import org.whuims.web.util.word2html.Word2Hml;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by Qikai on 2016/11/7.
 */
@Controller
@RequestMapping(value = "/doc")
public class DocumentController {

    private static final String PAGE_SIZE = "10";
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    @Autowired
    private DocumentService documentService;
    @Autowired
    private DocumentSolrService documentSolrService;
    @Autowired
    private DocumentExtractor documentExtractor;
    @Autowired
    private Pdf2Xml pdf2Xml;
    @Autowired
    private Word2Hml word2Hml;
    @Autowired
    private ParagraphExtractor paragraphExtractor;
    @Autowired
    private ParagraphService paragraphService;
    @Autowired
    private ParagraphSolrService paragraphSolrService;
    private Set<Long> toUpdateDocIds = new HashSet<>();

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }

    @RequestMapping(value = "list", method = RequestMethod.GET)
    public String list(@RequestParam(value = "page", defaultValue = "1") int pageNumber,
                       @RequestParam(value = "page.size", defaultValue = PAGE_SIZE) int pageSize, Model model,
                       ServletRequest request) {
        Map<String, Object> searchParams = Servlets.getParametersStartingWith(request, "search_");
//        searchParams.put("user_id", String.valueOf(getCurrentUserId()));
        Page<Document> docs;
        if (getCurrentUserName().equals("admin")) {
            docs = documentService.getDocumentList(searchParams, pageNumber, pageSize, null);
        } else {
            docs = documentService.findAllByUserId(getCurrentUserId(), pageNumber, pageSize, null);
        }
        model.addAttribute("docs", docs);
        model.addAttribute("sortType", "auto");
        // 将搜索条件编码成字符串，用于排序，分页的URL
        model.addAttribute("searchParams", Servlets.encodeParameterStringWithPrefix(searchParams, "search_"));
        if (toUpdateDocIds.size() != 0) {
            toUpdateDocIds.removeAll(toUpdateDocIds);
        }
        return "doc/doclist";
    }

    @RequestMapping(value = "detail/{id}", method = RequestMethod.GET)
    public String detail(@PathVariable("id") Long id, Model model) {
        Document doc = documentService.getDocument(id);
        model.addAttribute("doc", doc);
        URL apath = DocumentController.class.getClassLoader().getResource("/");
        File dir = new File(apath.getPath()).getParentFile().getParentFile();
        String path = dir.getAbsolutePath() + "/resource/docs/" + doc.getLocation();
        File file = new File(path);
        System.out.println(file.getAbsolutePath());
        String exists = null;
        String xmlExists = null;
        String htmlExists = null;
        if (file.exists() && doc.getLocation().toLowerCase().contains(".pdf")) {
            exists = "true";
        }
        if (!Strings.isNullOrEmpty(doc.getXmlText())) {
            xmlExists = "true";
            String xmlText = doc.getXmlText();
            String xmlTextPure = xmlText.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\\\"", "&quot;");
            doc.setXmlTextPure(xmlTextPure);
        }
        if (!Strings.isNullOrEmpty(doc.getHtmlText())) {
            htmlExists = "true";
            String htmlText = doc.getHtmlText();
            String htmlTextPure = htmlText.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\\\"", "&quot;");
            doc.setHtmlTextPure(htmlTextPure);
        }
        model.addAttribute("exists", exists);
        model.addAttribute("xmlExists", xmlExists);
        model.addAttribute("htmlExists", htmlExists);
        return "doc/docdetail";
    }

    @RequestMapping(value = "elements/{id}", method = RequestMethod.GET)
    public String elements(@PathVariable("id") Long id, Model model) {
        Document doc = documentService.getDocument(id);
        model.addAttribute("doc", doc);
        return "doc/elements";
    }

    @RequestMapping(value = "create", method = RequestMethod.GET)
    public String createForm(Model model) {
        model.addAttribute("doc", new Document());
        model.addAttribute("action", "create");
        return "doc/doccreate";
    }

    @RequestMapping(value = "update/{id}", method = RequestMethod.GET)
    public String edit(@PathVariable("id") Long id, Model model) {
        Document doc = documentService.getDocument(id);
        model.addAttribute("doc", doc);
        return "doc/docedit";
    }

    @RequestMapping(value = "delete/{id}", method = RequestMethod.GET)
    public String delete(@PathVariable("id") Long id, Model model) {
        documentService.delete(id);
        paragraphService.deleteByDocumentId(id);
        documentSolrService.delete(String.valueOf(id));
        paragraphSolrService.deleteByDocumentId(id);
        return "redirect:/doc/list";
    }

    @RequestMapping(value = "update", method = RequestMethod.POST)
    public String update(@Valid @ModelAttribute("document") Document document, RedirectAttributes redirectAttributes) {
        Document originalDocument = documentService.getDocument(document.getId());
        document.setHtmlText(originalDocument.getHtmlText());
        document.setXmlText(originalDocument.getXmlText());
        documentService.save(document);
        redirectAttributes.addFlashAttribute("message", "编辑文档信息成功");
        return "redirect:list";
    }

    @RequestMapping(value = "writeDoc", method = RequestMethod.GET)
    public String writeDoc(Model model) {
        List<Document> documents = new ArrayList<>();
        for (Long id : toUpdateDocIds) {
            Document document = documentService.getDocument(id);
            documents.add(document);
        }
        model.addAttribute("docs", documents);
        return "doc/docwrite";
    }

    @RequestMapping(value = "addUpdateDoc/{id}", method = RequestMethod.GET)
    public void addUpdateDoc(@PathVariable("id") Long id) {
        toUpdateDocIds.add(id);
    }

    @RequestMapping(value = "save", method = RequestMethod.POST)
    public String create(@Valid @ModelAttribute("document") Document document, RedirectAttributes redirectAttributes) {
        if (document.getCreateDate() == null) {
            document.setCreateDate(new Date());
        }
        if (!Strings.isNullOrEmpty(document.getDocTimeStr())) {
            try {
                Date docTime = simpleDateFormat.parse(document.getDocTimeStr());
                document.setDocTime(docTime);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        Long userId = getCurrentUserId();
        if (userId != null)
            document.setUserId(userId);
        String content = "";
        String xmlOrHtml = "";
        if (!Strings.isNullOrEmpty(document.getLocation())) {
            String location = document.getLocation();
            String[] arr = location.split("\\.");
            String docType = arr[arr.length - 1];
            docType = docType.toLowerCase();
            document.setDocType(docType);
        }
        boolean isPdf = document.getLocation().toLowerCase().contains(".pdf");
        if (!Strings.isNullOrEmpty(document.getLocation())) {
            content = documentExtractor.extract(document.getLocation());
            document.setContent(content);
            if (isPdf) {
                xmlOrHtml = pdf2Xml.pdf2Xml(document.getLocation());
                document.setXmlText(xmlOrHtml);
                documentService.save(document);
                List<Paragraph> paragraphs = paragraphExtractor.extractPdf(xmlOrHtml, document.getId());
                for (Paragraph paragraph : paragraphs) {
                    paragraph.setUserId(getCurrentUserId());
                }
                paragraphService.save(paragraphs);
            } else try {
                xmlOrHtml = word2Hml.extract(document.getLocation());
                document.setHtmlText(xmlOrHtml);
                documentService.save(document);
                List<Paragraph> paragraphs = paragraphExtractor.extractWord(content, document.getId());
                for (Paragraph paragraph : paragraphs) {
                    paragraph.setUserId(getCurrentUserId());
                }
                paragraphService.save(paragraphs);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        } else
            documentService.save(document);
        redirectAttributes.addFlashAttribute("message", "编辑文档信息成功");
        return "redirect:list";
    }

    @RequestMapping(value = "search", method = RequestMethod.GET)
    public String search(@RequestParam(value = "page", defaultValue = "1") int pageNumber,
                         @RequestParam(value = "page.size", defaultValue = PAGE_SIZE) int pageSize, Model model,
                         ServletRequest request) {
        String keyword = request.getParameter("keyword");
        if (Strings.isNullOrEmpty(keyword)) {
            keyword = "*";
        }
        Page<Document> docs = documentSolrService.findByTemplate(new PageRequest(pageNumber - 1, pageSize), keyword);
        model.addAttribute("docs", docs);
        model.addAttribute("sortType", "auto");
        // 将搜索条件编码成字符串，用于排序，分页的URL
        model.addAttribute("keyword", keyword);
        model.addAttribute("total", docs.getTotalElements());
        if (toUpdateDocIds.size() != 0) {
            toUpdateDocIds.removeAll(toUpdateDocIds);
        }
        return "doc/search_result_simple";
    }

    @RequestMapping(value = "advancedSearch", method = RequestMethod.GET)
    public String advancedSearch(@RequestParam(value = "page", defaultValue = "1") int pageNumber,
                                 @RequestParam(value = "page.size", defaultValue = PAGE_SIZE) int pageSize, Model model, ServletRequest request) {
        DocumentVo documentVo = new DocumentVo();
        documentVo.setTitle(request.getParameter("title"));
        documentVo.setContent(request.getParameter("content"));
        documentVo.setSummary(request.getParameter("summary"));
        documentVo.setAuthors(request.getParameter("authors"));
        documentVo.setKeywords(request.getParameter("keywords"));
        documentVo.setTags(request.getParameter("tags"));
        try {
            if (!Strings.isNullOrEmpty(request.getParameter("startCreateDate")))
                documentVo.setStartCreateDate(simpleDateFormat.parse(request.getParameter("startCreateDate")));
            if (!Strings.isNullOrEmpty(request.getParameter("endCreateDate")))
                documentVo.setEndCreateDate(simpleDateFormat.parse(request.getParameter("endCreateDate")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (!Strings.isNullOrEmpty(request.getParameter("startDocTime")))
                documentVo.setStartDocTime(simpleDateFormat.parse(request.getParameter("startDocTime")));
            if (!Strings.isNullOrEmpty(request.getParameter("endDocTime")))
                documentVo.setEndDocTime(simpleDateFormat.parse(request.getParameter("endDocTime")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        Page<Document> docs = documentSolrService.findByTemplate(new PageRequest(pageNumber - 1, pageSize), documentVo);
        model.addAttribute("docs", docs);
        model.addAttribute("sortType", "auto");
        StringBuilder stringBuilder = new StringBuilder();
        if (!Strings.isNullOrEmpty(documentVo.getTitle())) {
            stringBuilder.append("title=" + documentVo.getTitle());
        }
        if (!Strings.isNullOrEmpty(documentVo.getContent())) {
            if (!Strings.isNullOrEmpty(stringBuilder.toString()))
                stringBuilder.append("&content=" + documentVo.getContent());
            else stringBuilder.append("content=" + documentVo.getContent());
        }
        if (documentVo.getStartCreateDate() != null) {
            if (!Strings.isNullOrEmpty(stringBuilder.toString())) {
                if (!Strings.isNullOrEmpty(stringBuilder.toString()))
                    stringBuilder.append("&startCreateDate=" + documentVo.getStartCreateDate().toLocaleString());
                else stringBuilder.append("startCreateDate=" + documentVo.getStartCreateDate().toLocaleString());
            }
        }
        if (documentVo.getEndCreateDate() != null) {
            if (!Strings.isNullOrEmpty(stringBuilder.toString()))
                stringBuilder.append("&endCreateDate=" + documentVo.getEndCreateDate().toLocaleString());
            else stringBuilder.append("endCreateDate=" + documentVo.getEndCreateDate().toLocaleString());
        }
        model.addAttribute("searchParams", stringBuilder.toString());
        model.addAttribute("total", docs.getTotalElements());
        if (toUpdateDocIds.size() != 0) {
            toUpdateDocIds.removeAll(toUpdateDocIds);
        }
        return "doc/search_result_adv";
    }

    @RequestMapping(value = "seniorSearch", method = RequestMethod.GET)
    public String seniorSearch() {
        return "doc/advanced_search";
    }

    @ModelAttribute
    public void getDocument(@RequestParam(value = "id", defaultValue = "-1") Long id, Model model) {
        if (id != -1) {
            model.addAttribute("sdocument", documentService.getDocument(id));
        }
    }

    @RequestMapping(value = "write", method = RequestMethod.GET)
    public String write(Model model) {
        List<Document> documents = documentService.findTopDocuments(10);
        model.addAttribute("docs", documents);
        return "doc/docwrite";
    }

    @RequestMapping(value = "getDoc", method = RequestMethod.GET)
    public void write(HttpServletRequest request, HttpServletResponse response) {
        String docId = request.getParameter("docId");
        Long documentId = Long.parseLong(docId);
        Document document = documentService.getDocument(documentId);
        OutputStream out;
        try {
            out = response.getOutputStream();
            String content = JSON.toJSONString(document);
            out.write(content.getBytes("UTF-8"));
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "updateDoc", method = RequestMethod.POST)
    public String updateDoc(@RequestBody String json) {
        try {
            json = URLDecoder.decode(json, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Pattern idPattern = Pattern.compile("id=(.+?)&", Pattern.DOTALL);
        Pattern summaryPattern = Pattern.compile("summary=(.+?)&content=", Pattern.DOTALL);
        Pattern contentPattern = Pattern.compile("content=(.+?)&keynote=", Pattern.DOTALL);
        String docIdStr = "";
        String docSummaryStr = "";
        String docContentStr = "";
        String docKeynoteStr = "";
        Matcher matcher = idPattern.matcher(json);
        if (matcher.find()) {
            docIdStr = matcher.group(1);
        }
        matcher = summaryPattern.matcher(json);
        if (matcher.find()) {
            docSummaryStr = matcher.group(1);
        }
        matcher = contentPattern.matcher(json);
        if (matcher.find()) {
            docContentStr = matcher.group(1);
        }
        if (json.split("&keynote=").length > 1)
            docKeynoteStr = json.split("&keynote=")[1];
        Long docId = Long.parseLong(docIdStr);
        toUpdateDocIds.remove(docId);
        Document document = documentService.getDocument(docId);
        document.setKeynote(docKeynoteStr);
        document.setSummary(docSummaryStr);
        document.setContent(docContentStr);
        documentService.save(document);
        return "redirect:writeDoc";
    }

    @RequestMapping(value = "searchentry", method = RequestMethod.GET)
    public String simpleSearch() {
        return "doc/search";
    }

    /**
     * 取出Shiro中的当前用户Id.
     */
    private Long getCurrentUserId() {
        ShiroDbRealm.ShiroUser user = (ShiroDbRealm.ShiroUser) SecurityUtils.getSubject().getPrincipal();
        if (user == null) {
            return null;
        } else
            return user.id;
    }

    private String getCurrentUserName() {
        ShiroDbRealm.ShiroUser user = (ShiroDbRealm.ShiroUser) SecurityUtils.getSubject().getPrincipal();
        if (user == null)
            return null;
        else return user.loginName;
    }

}
